﻿/**
 * Copyright (c) 2008 Justin Etheredge http://www.codethinked.com
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Microsoft Public License (Ms-PL)
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/ms-pl.html
 * If redistributing this code, this entire header must remain intact.
 * 
 * Some of the code in this application has been taken 
 * from the following sources (all of which are licensed under
 * the Ms-PL):
 * Matt Warren's Blog: http://blogs.msdn.com/mattwar/
 * MSDN: http://msdn2.microsoft.com/en-us/library/bb546158.aspx
 */

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using LinqToSimpleDB;
using LinqToSimpleDB.Exceptions;

namespace LinqToSimpleDBTest
{
  /// <summary>
  /// Summary description for QueryTest
  /// </summary>
  [TestClass]
  public class QueryTest
  {


    public QueryTest()
    {
    }

    private TestContext testContextInstance;

    /// <summary>
    ///Gets or sets the test context which provides
    ///information about and functionality for the current test run.
    ///</summary>
    public TestContext TestContext
    {
      get
      {
        return testContextInstance;
      }
      set
      {
        testContextInstance = value;
      }
    }

    #region Additional test attributes
    #endregion

    [TestMethod]
    [ExpectedException(typeof(LinqToSimpleDB.Exceptions.InvalidAttributeException))]
    public void ThrowInvalidAttributeExceptionAfterUsingTwoAttributesInOneQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == "testvalue1"
                 && g.Attributes["test2"] == "testvalue4"
               select g);

      string query = GetQueryText(p);
    }

    [TestMethod]
    [ExpectedException(typeof(LinqToSimpleDB.Exceptions.InvalidAttributeException))]
    public void ThrowInvalidAttributeExceptionAfterUsingMultipleAttributesInOneQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == "testvalue1"
                 && g.Attributes["test1"] == "testvalue4"
                 && g.Attributes["test2"] == "testvalue2"
                 || g.Attributes["test3"] == "testvalue3"
               select g);

      string query = GetQueryText(p);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeEqualsQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == "testvalue1"
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' = 'testvalue1']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleVariableAttributeEqualsQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      string attributeName = "test1";
      var p = (from g in context.Domain
               where
                 g.Attributes[attributeName] == "testvalue1"
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' = 'testvalue1']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeIntegerEqualsQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == 15
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' = '000000000000000000000000000015.0000000000']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeIntegerVariableEqualsQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      int value = 15;
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == value
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' = '000000000000000000000000000015.0000000000']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeNotEqualsQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] != "testvalue1"
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' != 'testvalue1']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeGreaterThanQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] > "testvalue1"
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' > 'testvalue1']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeGreaterThanEqualToQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] >= "testvalue1"
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' >= 'testvalue1']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeLessThanQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] < "testvalue1"
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' < 'testvalue1']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeLessThanEqualToQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] <= "testvalue1"
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' <= 'testvalue1']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeAndQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == "testvalue1"
                 && g.Attributes["test1"] == "testvalue4"
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' = 'testvalue1' and 'test1' = 'testvalue4']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeNotAndQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 !(g.Attributes["test1"] == "testvalue1"
                 && g.Attributes["test1"] == "testvalue4")
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("not ['test1' = 'testvalue1' and 'test1' = 'testvalue4']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeOrQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == "testvalue1"
                 || g.Attributes["test1"] == "testvalue4"
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' = 'testvalue1' or 'test1' = 'testvalue4']", query);
    }

    [TestMethod]
    public void GetQueryTextFromSingleAttributeCombinedOrAndQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == "testvalue1"
                 || g.Attributes["test1"] == "testvalue2"
                 && g.Attributes["test1"] == "testvalue3"
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' = 'testvalue1' or 'test1' = 'testvalue2' and 'test1' = 'testvalue3']", query);
    }

    [TestMethod]
    public void GenerateEmptyQueryFromSimpleSelect()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain select g);
      string query = GetQueryText(p);
      Assert.IsTrue(String.IsNullOrEmpty(query), "Simple Query did not return an empty SimpleDB query.");
    }

    [TestMethod]
    public void GetQueryTextFromStartsWithQuery()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"].StartsWith("testvalue1")
               select g);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' starts-with 'testvalue1']", query);
    }

    //[TestMethod]
    //public void GetQueryTextFromSimpleProjectedSelect()
    //{
    //  LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
    //  var p = (from g in context.Domain select new { test1 = g.Attributes["test1"], test2 = g.Attributes["test2"] });
    //  string query = GetQueryText(p);
    //  Assert.IsTrue(String.IsNullOrEmpty(query), "Simple Query did not return an empty SimpleDB query.");
    //}

    [TestMethod]
    public void GetQueryTextFromTwoSingleAttributeAndQueriesWithUnion()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == "testvalue1"
                 && g.Attributes["test1"] == "testvalue4"
               select g)
               .Union(from h in context.Domain
                      where
                        h.Attributes["test2"] == "testvalue2"
                        && h.Attributes["test2"] == "testvalue5"
                      select h);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' = 'testvalue1' and 'test1' = 'testvalue4'] UNION ['test2' = 'testvalue2' and 'test2' = 'testvalue5']", query);
    }

    [TestMethod]
    public void GetQueryTextFromTwoSingleAttributeAndQueriesWithIntersect()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == "testvalue1"
                 && g.Attributes["test1"] == "testvalue4"
               select g)
               .Intersect(from h in context.Domain
                          where
                            h.Attributes["test2"] == "testvalue2"
                            && h.Attributes["test2"] == "testvalue5"
                          select h);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' = 'testvalue1' and 'test1' = 'testvalue4'] INTERSECT ['test2' = 'testvalue2' and 'test2' = 'testvalue5']", query);
    }

    [TestMethod]
    public void GetQueryTextFromThreeSingleAttributeAndQueriesWithUnionAndIntersect()
    {
      LinqToSimpleDB.SimpleDBContext context = TestSetup.GetContext("TestDomain");
      var p = (from g in context.Domain
               where
                 g.Attributes["test1"] == "testvalue1"
                 && g.Attributes["test1"] == "testvalue4"
               select g)
               .Intersect(from h in context.Domain
                          where
                            h.Attributes["test2"] == "testvalue2"
                            && h.Attributes["test2"] == "testvalue5"
                          select h)
               .Union(from i in context.Domain
                      where
                        i.Attributes["test3"] == "testvalue4"
                        || i.Attributes["test3"] == "testvalue8"
                      select i);

      string query = GetQueryText(p);
      Assert.AreEqual<string>("['test1' = 'testvalue1' and 'test1' = 'testvalue4'] INTERSECT ['test2' = 'testvalue2' and 'test2' = 'testvalue5'] UNION ['test3' = 'testvalue4' or 'test3' = 'testvalue8']", query);
    }

    private string GetQueryText<T>(IQueryable<T> query)
    {
      return ((LinqToSimpleDB.Query<T>)query).GetQueryText();
    }

    [TestMethod]
    public void ElementTypePropertyTest()
    {
      Query<SimpleDBItem> query = new Query<SimpleDBItem>();
      Assert.IsTrue(query.ElementType == typeof(SimpleDBItem),
        "ElementType does not match generic parameter.");
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentNullException))]
    public void NullProviderInSingleParamConstructorTest()
    {
      Query<SimpleDBItem> query = new Query<SimpleDBItem>(null);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentNullException))]
    public void NullProviderInTwoParamConstructorTest()
    {
      Query<SimpleDBItem> query = new Query<SimpleDBItem>(null, null);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentNullException))]
    public void NullExpressionInTwoParamConstructorTest()
    {
      SimpleDBContext context = TestSetup.GetContext("Test");
      SimpleDBQueryProvider queryProvider = new SimpleDBQueryProvider(context);
      Query<SimpleDBItem> query = new Query<SimpleDBItem>(queryProvider, null);
    }

    [TestMethod]
    [ExpectedException(typeof(InvalidQueryException))]
    public void NestedWhereTest()
    {
      SimpleDBContext context = TestSetup.GetContext("Test");
      var p = from g in context.Domain
              where (from a in context.Domain where 
                a.Attributes["test2"] == "test2" select a).Contains(g)
              select g;

      string queryText = GetQueryText(p);
    }

    [TestMethod]
    [ExpectedException(typeof(InvalidQueryException))]
    public void JoinTest()
    {
      SimpleDBContext context = TestSetup.GetContext("Test");
      var p = from g in context.Domain
              join f in context.Domain
              on g.Name equals f.Name
              where g.Attributes["test1"] == "attrib"
              select g;

      string queryText = GetQueryText(p);
    }

    [TestMethod]
    [ExpectedException(typeof(InvalidQueryException))]
    public void OrderByTest()
    {
      SimpleDBContext context = TestSetup.GetContext("Test");
      var p = from g in context.Domain
              orderby g.Attributes["attrib1"]
              select g;

      string queryText = GetQueryText(p);
    }

    [TestMethod]
    public void GetQueryTextFromBooleanQueryTest()
    {
      SimpleDBContext context = TestSetup.GetContext("Test");
      var p = from g in context.Domain
              where g.Attributes["attrib1"] == true || g.Attributes["attrib1"] == false
              select g;

      string queryText = GetQueryText(p);
      Assert.AreEqual<string>("['attrib1' = 'true' or 'attrib1' = 'false']", queryText);
    }
  }
}
